#pragma once
#include <string>
#include <iostream>
#include <fstream>

#include <stdio.h>
#include <time.h>
#include <stdarg.h>

#include "singleton.h"

namespace yx
{
	class Logger
	{
		friend class yx::pattern::Singleton<Logger>;
	public:

#define debug(format, ...) \
		yx::pattern::Singleton<Logger>::instance()->log(Logger::DEBUG, __FILE__, __LINE__, format, ##__VA_ARGS__)

#define info(format, ...) \
    yx::pattern::Singleton<Logger>::instance()->log(Logger::INFO, __FILE__, __LINE__, format, ##__VA_ARGS__)

#define warn(format, ...) \
    yx::pattern::Singleton<Logger>::instance()->log(Logger::WARN, __FILE__, __LINE__, format, ##__VA_ARGS__)

#define error(format, ...) \
    yx::pattern::Singleton<Logger>::instance()->log(Logger::ERROR, __FILE__, __LINE__, format, ##__VA_ARGS__)

#define fatal(format, ...) \
    yx::pattern::Singleton<Logger>::instance()->log(Logger::FATAL, __FILE__, __LINE__, format, ##__VA_ARGS__)

		enum Level
		{
			DEBUG = 0,
			INFO,
			WARN,
			ERROR,
			FATAL,
			LEVEL_COUNT
		};

		void log(Level level, const char* file, long line, const char* format, ...);

		bool open(const std::string& file_name)
		{
			_file_name = file_name;
			_fout.open(file_name, std::ios::app);

			return _fout.is_open();
		}

		void close()
		{
			_fout.close();
		}

	private:
		Logger();
		Logger(const std::string& file_name);
		~Logger();
		Logger(const Logger&) = delete;
		Logger& operator=(const Logger&) = delete;
	private:

		static const char* _level_hash[LEVEL_COUNT];

		std::string _file_name;
		std::ofstream _fout;
	};

	const char* Logger::_level_hash[LEVEL_COUNT] =
	{
		"DEBUG",
		"INFO",
		"WARN",
		"ERROR",
		"FATAL"
	};

	Logger::Logger()
	{}

	Logger::Logger(const std::string& file_name)
	{
		open(file_name);
	}

	Logger::~Logger()
	{
		close();
	}

	void Logger::log(Level level, const char* file, long line, const char* format, ...)
	{
		std::string str;

		time_t ticks = time(NULL);
		struct tm* ptm = localtime(&ticks);

		char timestamp[64];
		memset(timestamp, 0, sizeof(timestamp));

		strftime(timestamp, sizeof(timestamp), "[%Y-%m-%d %H:%M:%S]", ptm);

		str += timestamp;

		str += "[";
		str += _level_hash[level];
		str += "]";

		str += "[";
		str += file;
		str += " : ";
		str += std::to_string(line);
		str += "]";

		va_list arg_ptr;
		va_start(arg_ptr, format);

		int len = vsnprintf(NULL, 0, format, arg_ptr);
		va_end(arg_ptr);

		if (len > 0)
		{
			char* content = new char[len + 1];

			va_start(arg_ptr, format);
			len = vsnprintf(content, len + 1, format, arg_ptr);
			content[len] = '\0';
			va_end(arg_ptr);

			str += content;

			delete[] content;
		}

		_fout << str;
		_fout.flush();
	}
}